package com.code.soulnetty.modbus.client.codec;

import com.code.soulnetty.modbus.client.message.ProtocolResponse;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * request eg:
 * -------------------------------------------------------------------------------------------------------
 * ｜                   ADU_Header                          ｜                   PDU                      ｜
 * ｜_______________________________________________________｜____________________________________________｜
 * ｜ 单元事务标示符  ｜  协议标示符  ｜    报文长度  ｜  单元标示符 ｜  功能码    ｜   寄存器起始位置  ｜  寄存器大小    ｜
 * ｜    0x0001    ｜   0x0000    ｜    0x0006  ｜    0xff   ｜   0x03    ｜      0x0000     ｜   0x0002    ｜
 * ｜   (2byte)    ｜   (2byte)   ｜    (2byte) ｜   (1byte) ｜  (1byte)  ｜     (2byte)     ｜  (2byte)    ｜
 * --------------------------------------------------------------------------------------------------------
 *
 * response eg:
 * -------------------------------------------------------------------------------------------------------
 * ｜                   ADU_Header                          ｜                   PDU                      ｜
 * ｜_______________________________________________________｜____________________________________________｜
 * ｜ 单元事务标示符  ｜  协议标示符  ｜    报文长度  ｜  单元标示符 ｜  功能码    ｜   数据字节数量    ｜      数据    ｜
 * ｜    0x0001    ｜   0x0000    ｜    0x0006  ｜    0xff   ｜   0x03    ｜      0x04       ｜   0x0002... ｜
 * ｜   (2byte)    ｜   (2byte)   ｜    (2byte) ｜   (1byte) ｜  (1byte)  ｜     (1byte)     ｜    (Nbyte)  ｜
 * --------------------------------------------------------------------------------------------------------
 *
 * @author jdy
 * @date 2:05 下午
 **/
@Slf4j
public class ModbusTcpDecoder extends LengthFieldBasedFrameDecoder {

    private static final int ADU_LEN = 7;

    private final Codec<ByteBuf, ProtocolResponse> codec;


    public ModbusTcpDecoder(Codec<ByteBuf, ProtocolResponse> codec) {
        super(Short.MAX_VALUE, 4, 2);
        this.codec = codec;
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        final Object decode = super.decode(ctx, in);
        if (decode == null) {
            return null;
        }
        try {
            ByteBuf content = (ByteBuf) decode;
            final int curIndex = content.readerIndex();
            final int total = content.readableBytes();
            final ByteBuf adu = content.slice(curIndex, ADU_LEN);
            final ByteBuf pdu = content.slice(curIndex + ADU_LEN, total - ADU_LEN);
            return codec.decode(ctx, adu, pdu);
        } finally {
            ReferenceCountUtil.release(decode);
        }
    }

}
